<?php
class Wee_Logging_Model_Processor{
    protected $_config;
    protected $_eventConfig;
    protected $_controllerActionsHandler;
    protected $_modelsHandler;
    protected $_actionName = '';
    protected $_lastAction = '';
    protected $_initAction = '';
    protected $_skipNextAction = false;
    protected $_eventChanges = array();
    protected $_skipFields = array();
    protected $_skipFieldsByModel = array();
    protected $_collectedIds = array();
    protected $_collectedAdditionalData = array();
    const XML_PATH_SKIP_GLOBAL_FIELDS = 'adminhtml/wee/logging/skip_fields';
    public function __construct(){
        $this -> _config = Mage :: getSingleton('wee_logging/config');
        $this -> _modelsHandler = Mage :: getModel('wee_logging/handler_models');
        $this -> _controllerActionsHandler = Mage :: getModel('wee_logging/handler_controllers');
    }
    public function initAction($fullActionName, $actionName){
        $this -> _actionName = $actionName;
        if(!$this -> _initAction){
            $this -> _initAction = $fullActionName;
        }
        $this -> _lastAction = $fullActionName;
        $this -> _skipNextAction = (!$this -> _config -> isActive($fullActionName)) ? true : false;
        if ($this -> _skipNextAction){
            return;
        }
        $this -> _eventConfig = $this -> _config -> getNode($fullActionName);
        if ($doNotLog = Mage :: getSingleton('admin/session') -> getSkipLoggingAction()){
            if (is_array($doNotLog)){
                $key = array_search($fullActionName, $doNotLog);
                if ($key !== false){
                    unset($doNotLog[$key]);
                    Mage :: getSingleton('admin/session') -> setSkipLoggingAction($doNotLog);
                    $this -> _skipNextAction = true;
                    return;
                }
            }
        }
        if (isset($this -> _eventConfig -> skip_on_back)){
            $addValue = array_keys($this -> _eventConfig -> skip_on_back -> asArray());
            $sessionValue = Mage :: getSingleton('admin/session') -> getSkipLoggingAction();
            if (!is_array($sessionValue) && $sessionValue){
                $sessionValue = explode(',', $sessionValue);
            }elseif (!$sessionValue){
                $sessionValue = array();
            }
            $merge = array_merge($addValue, $sessionValue);
            Mage :: getSingleton('admin/session') -> setSkipLoggingAction($merge);
        }
    }
    public function modelActionAfter($model, $action){
        if (!Mage :: getModel('wee_logging/logging') -> hasValidLicense()){
            return;
        }
        if ($this -> _skipNextAction){
            return;
        }
        $usedModels = $defaultExpectedModels = null;
        if ($this -> _eventConfig){
            $actionGroupNode = $this -> _eventConfig -> getParent() -> getParent();
            if (isset($actionGroupNode -> expected_models)){
                $defaultExpectedModels = $actionGroupNode -> expected_models;
            }
        }
        $expectedModels = isset($this -> _eventConfig -> expected_models) ? $this -> _eventConfig -> expected_models : false;
        if (!$expectedModels || empty($expectedModels)){
            if (empty($defaultExpectedModels)){
                return;
            }
            $usedModels = $defaultExpectedModels;
        }else{
            if ($expectedModels -> getAttribute('extends') == 'merge'){
                $defaultExpectedModels -> extend($expectedModels);
                $usedModels = $defaultExpectedModels;
            }else{
                $usedModels = $expectedModels;
            }
        }
        $additionalData = $skipData = array();
        foreach ($usedModels -> children() as $expect => $callback){
            if (isset($callback -> skip_data)){
                $rawData = $callback -> skip_data -> asCanonicalArray();
                $skipData = array_unique(array_keys($rawData));
            }
            if (isset($callback -> additional_data)){
                $rawData = $callback -> additional_data -> asCanonicalArray();
                $additionalData = array_unique(array_keys($rawData));
            }
            $additionalData = array_diff($additionalData, $skipData);
            $className = Mage :: getConfig() -> getModelClassName(str_replace('__', '/', $expect));
            if ($model instanceof $className){
                $classMap = $this -> _getCallbackFunction(trim($callback), $this -> _modelsHandler, sprintf('model%sAfter', ucfirst($action)));
                $handler = $classMap['handler'];
                $callback = $classMap['callback'];
                if ($handler){
                    $this -> collectAdditionalData($model, $additionalData);
                    $changes = $handler -> $callback($model, $this);
                    if (is_object($changes)){
                        $changes -> cleanupData($skipData);
                        if ($changes -> hasDifference()){
                            $changes -> setSourceName($className);
                            $changes -> setSourceId($model -> getId());
                            $this -> addEventChanges($changes);
                        }
                    }
                }
            }
            $skipData = array();
        }
    }
    public function logAction(){
        if (!Mage :: getModel('wee_logging/logging') -> hasValidLicense()){
            return;
        }
        if (!$this -> _initAction){
            return;
        }
        $username = null;
        $userId = null;
        if (Mage :: getSingleton('admin/session') -> isLoggedIn()){
            $userId = Mage :: getSingleton('admin/session') -> getUser() -> getId();
            $username = Mage :: getSingleton('admin/session') -> getUser() -> getUsername();
        }
        $errors = Mage :: getModel('adminhtml/session') -> getMessages() -> getErrors();
        $loggingEvent = Mage :: getModel('wee_logging/event') -> setData(array('ip' => Mage :: helper('core/http') -> getRemoteAddr(), 'x_forwarded_ip' => Mage :: app() -> getRequest() -> getServer('HTTP_X_FORWARDED_FOR'), 'user' => $username, 'user_id' => $userId, 'is_success' => empty($errors), 'fullaction' => $this -> _initAction, 'error_message' => implode("\n", array_map(create_function('$a', 'return $a->toString();'), $errors)),));
        if ($this -> _actionName == 'denied'){
            $_conf = $this -> _config -> getNode($this -> _initAction);
            if (!$_conf || !$this -> _config -> isActive($this -> _initAction)){
                return;
            }
            $loggingEvent -> setAction($_conf -> action);
            $loggingEvent -> setEventCode($_conf -> getParent() -> getParent() -> getName());
            $loggingEvent -> setInfo(Mage :: helper('wee_logging') -> __('Access denied'));
            $loggingEvent -> setIsSuccess(0);
            $loggingEvent -> save();
            return;
        }
        if ($this -> _skipNextAction){
            return;
        }
        $loggingEvent -> setAction($this -> _eventConfig -> action);
        $loggingEvent -> setEventCode($this -> _eventConfig -> getParent() -> getParent() -> getName());
        try{
            $callback = isset($this -> _eventConfig -> post_dispatch) ? (string)$this -> _eventConfig -> post_dispatch : false;
            $defaulfCallback = 'postDispatchGeneric';
            $classMap = $this -> _getCallbackFunction($callback, $this -> _controllerActionsHandler, $defaulfCallback);
            $handler = $classMap['handler'];
            $callback = $classMap['callback'];
            if (!$handler){
                return;
            }
            if ($handler -> $callback($this -> _eventConfig, $loggingEvent, $this)){
                if ($this -> getCollectedAdditionalData()){
                    $loggingEvent -> setAdditionalInfo($this -> getCollectedAdditionalData());
                }
                $loggingEvent -> save();
                if ($eventId = $loggingEvent -> getId()){
                    foreach ($this -> _eventChanges as $changes){
                        if ($changes && ($changes -> getOriginalData() || $changes -> getResultData())){
                            $changes -> setEventId($eventId);
                            $changes -> save();
                        }
                    }
                }
            }
        }
        catch (Exception $e){
            Mage :: logException($e);
        }
    }
    public function collectId($model){
        $this -> _collectedIds[get_class($model)][] = $model -> getId();
    }
    public function getCollectedIds(){
        $ids = array();
        foreach ($this -> _collectedIds as $className => $classIds){
            $uniqueIds = array_unique($classIds);
            $ids = array_merge($ids, $uniqueIds);
            $this -> _collectedIds[$className] = $uniqueIds;
        }
        return $ids;
    }
    public function collectAdditionalData($model, array $attributes){
        $attributes = array_unique($attributes);
        if ($model -> getId()){
            foreach ($attributes as $attribute){
                $value = $model -> getDataUsingMethod($attribute);
                if (!empty($value)){
                    $this -> _collectedAdditionalData[get_class($model)][$model -> getId()][$attribute] = $value;
                }
            }
        }
    }
    public function getCollectedAdditionalData(){
        return $this -> _collectedAdditionalData;
    }
    protected function _getCallbackFunction($srtCallback, $defaultHandler, $defaultFunction){
        $return = array('handler' => $defaultHandler, 'callback' => $defaultFunction);
        if (empty($srtCallback)){
            return $return;
        }
        try{
            $classPath = explode('::', $srtCallback);
            if (count($classPath) == 2){
                $return['handler'] = Mage :: getSingleton(str_replace('__', '/', $classPath[0]));
                $return['callback'] = $classPath[1];
            }else{
                $return['callback'] = $classPath[0];
            }
            if (!$return['handler'] || !$return['callback'] || !method_exists($return['handler'], $return['callback'])){
                Mage :: throwException("Unknown callback function: {$srtCallback}");
            }
        }
        catch (Exception $e){
            $return['handler'] = false;
            Mage :: logException($e);
        }
        return $return;
    }
    public function cleanupData($data){
        if (!$data && !is_array($data)){
            return array();
        }
        $clearData = array();
        foreach ($data as $key => $value){
            if (!in_array($key, $this -> _skipFields) && !in_array($key, $this -> _skipFieldsByModel) && !is_array($value) && !is_object($value)){
                $clearData[$key] = $value;
            }
        }
        return $clearData;
    }
    public function addEventChanges($eventChange){
        $this -> _eventChanges[] = $eventChange;
        return $this;
    }
}
